﻿namespace WPForms.Phone.ViewModel
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Linq;
    using System.Net;
    using System.ServiceModel;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Ink;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Animation;
    using System.Windows.Shapes;
    using Microsoft.Practices.Prism.Commands;
    using Microsoft.Practices.Prism.ViewModel;
    using PhoneUtils.Services;
    using PhoneUtils.ViewModel;
    using WPForms;
    using WPForms.Common.Model;
    using WPForms.Common.Services;

    /// <summary>
    /// View model responsible for showing a form, or a part of a form. Also handles navigations between form elements.
    /// </summary>
    public class FormViewModel : NotificationObject, INavigationAware
    {
        /// <summary>
        /// The current form
        /// </summary>
        private FormInstance currentForm;

        /// <summary>
        /// The current section.
        /// </summary>
        private FormSection currentSection;

        /// <summary>
        /// Reference to the form store
        /// </summary>
        private IFormStore formStore;

        /// <summary>
        /// Reference to the navigation service.
        /// </summary>
        private PhoneUtils.Services.INavigationService navigationService;

        /// <summary>
        /// Initializes a new instance of the <see cref="FormViewModel"/> class.
        /// </summary>
        /// <param name="navigationService">The navigation service.</param>
        /// <param name="formStore">The form store.</param>
        public FormViewModel(INavigationService navigationService, IFormStore formStore)
        {
            this.formStore = formStore;
            this.BackStack = new ObservableCollection<FormSection>();
            this.navigationService = navigationService;
            this.SectionSelectCommand = new DelegateCommand<FormSection>(
                section =>
                {
                    var uri = string.Format("/WPForms.Phone;component/View/Form.xaml?section={0}&formId={1}", section.Key, this.CurrentForm.Id);
                    this.navigationService.Navigate(new Uri(uri, UriKind.Relative));
                });
            this.DeleteCommand = new DelegateCommand(this.Delete);
            this.UploadCommand = new DelegateCommand(this.UploadToServer, () => this.CurrentForm != null && !string.IsNullOrEmpty(this.CurrentForm.ServerUrl));
        }

        /// <summary>
        /// Gets the back stack of the form sections.
        /// </summary>
        public ObservableCollection<FormSection> BackStack { get; private set; }

        /// <summary>
        /// Gets the command responsible for selecting a section.
        /// </summary>
        public ICommand SectionSelectCommand { get; private set; }

        /// <summary>
        /// Gets the command responsible for saving the form.
        /// </summary>
        public DelegateCommand UploadCommand { get; private set; }

        /// <summary>
        /// Gets the command responsible for deleting a form instance.
        /// </summary>
        public ICommand DeleteCommand { get; private set; }

        /// <summary>
        /// Gets the current section.
        /// </summary>
        public FormSection CurrentSection
        {
            get
            {
                return this.currentSection;
            }

            private set
            {
                this.currentSection = value;
                this.RaisePropertyChanged(() => this.CurrentSection);
            }
        }

        /// <summary>
        /// Gets the current form.
        /// </summary>
        public FormInstance CurrentForm
        {
            get
            {
                return this.currentForm;
            }

            private set
            {
                this.currentForm = value;
                this.RaisePropertyChanged(() => this.CurrentForm);
            }
        }

        /// <summary>
        /// Raises the <see cref="E:NavigatedFrom"/> event.
        /// </summary>
        /// <param name="navigationArgs">The <see cref="System.Windows.Navigation.NavigationEventArgs"/> instance containing the event data.</param>
        /// <param name="context">The context.</param>
        public void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs navigationArgs, System.Windows.Navigation.NavigationContext context)
        {
        }

        /// <summary>
        /// Raises the <see cref="E:NavigatedTo"/> event.
        /// </summary>
        /// <param name="navigationArgs">The <see cref="System.Windows.Navigation.NavigationEventArgs"/> instance containing the event data.</param>
        /// <param name="context">The context.</param>
        public void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs navigationArgs, System.Windows.Navigation.NavigationContext context)
        {
            if (context.QueryString.ContainsKey("formId"))
            {
                var formId = int.Parse(context.QueryString["formId"]);
                if (this.CurrentForm == null || this.CurrentForm.Id != formId)
                {
                    this.CurrentForm = this.formStore.LoadForm(formId);
                    this.UploadCommand.RaiseCanExecuteChanged();
                }
            }

            if (context.QueryString.ContainsKey("section"))
            {
                this.CurrentSection = this.CurrentForm.FindElementByKey<FormSection>(context.QueryString["section"]);
            }
            else
            {
                this.CurrentSection = this.CurrentForm.MainSection;
            }
        }

        /// <summary>
        /// Raises the <see cref="E:NavigatingFrom"/> event.
        /// </summary>
        /// <param name="navigationArgs">The <see cref="System.Windows.Navigation.NavigatingCancelEventArgs"/> instance containing the event data.</param>
        /// <param name="context">The context.</param>
        public void OnNavigatingFrom(System.Windows.Navigation.NavigatingCancelEventArgs navigationArgs, System.Windows.Navigation.NavigationContext context)
        {
            if (this.CurrentSection != null)
            {
                navigationArgs.Cancel = !this.GetValidationResult(this.CurrentSection, "Leave this page?");
                this.formStore.UpdateForm(this.currentForm);
            }
        }

        /// <summary>
        /// Raises the <see cref="E:FragmentNavigation"/> event.
        /// </summary>
        /// <param name="navigationArgs">The <see cref="System.Windows.Navigation.FragmentNavigationEventArgs"/> instance containing the event data.</param>
        /// <param name="context">The context.</param>
        public void OnFragmentNavigation(System.Windows.Navigation.FragmentNavigationEventArgs navigationArgs, System.Windows.Navigation.NavigationContext context)
        {
        }

        /// <summary>
        /// Deletes the current form
        /// </summary>
        private void Delete()
        {
            if (MessageBox.Show("Are you sure?", "Delete form", MessageBoxButton.OK) == MessageBoxResult.OK)
            {
                this.formStore.DeleteForm(this.CurrentForm);
                this.currentForm = null;
                this.currentSection = null;
                this.navigationService.Navigate(new Uri("/WPForms.Phone;component/View/FormCollection.xaml", UriKind.Relative));
            }
        }

        /// <summary>
        /// Gets the validation result.
        /// </summary>
        /// <param name="element">The element to validate.</param>
        /// <param name="question">The question to ask if there is a validation problem.</param>
        /// <returns>
        ///   <c>true</c> if the process can proceed; false otherwise.
        /// </returns>
        private bool GetValidationResult(FormElement element, string question)
        {
            var validation = element.Validate().ToList();
            if (validation.Count > 0)
            {
                validation.Add(Environment.NewLine + question);
                return MessageBox.Show(string.Join(Environment.NewLine, validation), "Input Errors", MessageBoxButton.OKCancel) == MessageBoxResult.OK;
            }

            return true;
        }

        /// <summary>
        /// Uploads the current form data to the specified server.
        /// </summary>
        private void UploadToServer()
        {
            if (this.GetValidationResult(this.CurrentForm.MainSection, "Upload anyway?"))
            {
                var formStoreclient = new FormsService.FormStoreClient(new BasicHttpBinding(), new EndpointAddress(this.currentForm.ServerUrl));
                formStoreclient.UpdateFormCompleted += new EventHandler<System.ComponentModel.AsyncCompletedEventArgs>(this.FormStoreClient_UpdateFormCompleted);
                formStoreclient.UpdateFormAsync(this.CurrentForm);
            }
        }

        /// <summary>
        /// Handles the UpdateFormCompleted event of the FormStoreClient control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.ComponentModel.AsyncCompletedEventArgs" /> instance containing the event data.</param>
        private void FormStoreClient_UpdateFormCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                MessageBox.Show("Upload complete.");
            }
            else
            {
                MessageBox.Show("Upload failed, please try again later.");
            }
        }
    }
}
